﻿using System;
using System.IO;
using System.Text;

namespace GLDotNet
{
    public class Shader : GLObject
    {       
        public ShaderType ShaderType
        {
            get;
            private set;
        }

        public bool IsCompiled
        {
            get;
            private set;
        }

        public Shader(GraphicsDevice graphicsDevice, ShaderType shaderType)
            : base(graphicsDevice)
        {            
            this.ShaderType = shaderType;

            this.Handle = this.Context.CreateShader(shaderType);
        }

        protected override void DeleteObject()
        {
            this.Context.DeleteShader(this.Handle);
        }

        public static Shader CompileFromFile(GraphicsDevice context, ShaderType shaderType, string fileName)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            if (fileName == null)
                throw new ArgumentNullException("fileName");

            using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read))
                return CompileFromStream(context, shaderType, file);
        }

        public static Shader CompileFromStream(GraphicsDevice context, ShaderType shaderType, Stream stream)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            if (stream == null)
                throw new ArgumentNullException("stream");

            StreamReader reader = new StreamReader(stream);
            string source = reader.ReadToEnd();

            Shader shader = new Shader(context, shaderType);

            shader.SetSource(source);
            shader.Compile();

            return shader;
        }

        public void SetSource(string source)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            if (this.IsCompiled)
                throw new InvalidOperationException("Shader source may not be changed once a shader has been compiled.");

            if (source == null)
                throw new ArgumentNullException("source");

            string[] shaderSources = new string[] { source };
            int length = source.Length;

            this.Context.ShaderSource(this.Handle, 1, shaderSources, ref length);
        }

        public void Compile()
        {
            if (this.IsCompiled)
                throw new InvalidOperationException("Shader has already compiled.");

            this.Context.CompileShader(this.Handle);

            int compileStatus;
            this.Context.GetShader(this.Handle, GL.CompileStatus, out compileStatus);

            if (compileStatus == 0)
            {
                StringBuilder buffer = new StringBuilder(1024);
                int length;

                this.Context.GetShaderInfoLog(this.Handle, buffer.Capacity, out length, buffer);

                throw new InvalidOperationException("Failed to compile shader: " + Environment.NewLine + buffer.ToString());
            }

            this.IsCompiled = true;
        }
    }
}
